通过DDCTF两道逆向题练习脱壳
明天出发去打西湖论剑线下了,然后今天守望又出了PVE,心有点不定,啥都不想做。。。不想浪费时间,刚好最近做了DDCTF的前两道逆向都是脱壳的,手动脱壳还没试过,就记下来。(其实是因为第一题UPX工具脱壳脱出来不好所以手动orz)
前置工作:¶
之前还没用过IDA在windows上调试,都是用OD或者xdbg。这次本来也想在OD脱的,结果太菜了一进去就不知道是什么情况了。。。想着还是IDA舒服,趁机学一下。结合IDA在linux的调试要开调试进程,在IDA的文件夹下搜到了这个:
这题是32位的,所以运行最上面的那个。然后在IDA选择Debugger->Select debugger->Local Windows debugger。
reverse1_final:¶
先IDA分析,段名称叫UPX1、UPX2,因此很大可能是UPX壳。PEID查了一下也确实是UPX壳。前面一堆压缩的信息,没什么意义。直接看后面。
一开始就是个pusha,所以可以用esp定律脱壳,可以在pusha后,对当前esp所在位置下硬件访问断点。F9执行。
F9完跳到0x917C47,后面有个小循环,所以在0x917C51下个断点再F9,就出来了。一直单步,看到运行到12AA就停了,看控制台等待输入,因此这个应该是主要运行程序。跟进去看,发现能F5了,而且有提示输出和读入数据,因此这个大概率是main函数。但是只有printf和scanf,感觉分析不完全,再到汇编看看。
看上去不是正常的结尾。再往后看还有一些零零散散的程序。对程序碎片进行C操作,组合成程序块方便后面操作。因此把当前函数和后面的部分函数U掉,再重新P一下进行函数分析,F5看下,就能得到看上去较为正常的程序。
看上去主要的运算都在DD1000那个函数,进去看看。点进去看什么都没有,到汇编层面看看。看到也是零零散散的程序。C一下直接对DD1000函数进行F5,直接能分析出来了。
后面都是非常简单的逻辑了。将输入和常量表操作一下,然后和DDCTF{reverseME}进行比较,正确就过check了。只要逆运算一下就能得到正确输入,即flag。
脚本:
flag="DDCTF{reverseME}" constant=[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xBA,0xA5,0x0D,0x34,0x45,0x5A,0xF2,0xCB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFE,0xFF,0xFF,0xFF,0x01,0x00,0x00,0x00,0x7E,0x7D,0x7C,0x7B,0x7A,0x79,0x78,0x77, 0x76,0x75,0x74,0x73,0x72,0x71,0x70,0x6F,0x6E,0x6D,0x6C,0x6B,0x6A,0x69,0x68,0x67, 0x66,0x65,0x64,0x63,0x62,0x61,0x60,0x5F,0x5E,0x5D,0x5C,0x5B,0x5A,0x59,0x58,0x57, 0x56,0x55,0x54,0x53,0x52,0x51,0x50,0x4F,0x4E,0x4D,0x4C,0x4B,0x4A,0x49,0x48,0x47, 0x46,0x45,0x44,0x43,0x42,0x41,0x40,0x3F,0x3E,0x3D,0x3C,0x3B,0x3A,0x39,0x38,0x37, 0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x2F,0x2E,0x2D,0x2C,0x2B,0x2A,0x29,0x28,0x27, 0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x00,0x01,0x00,0x00,0x00,0x00,0x1B,0xD5,0x00] print len(constant) origin="" for i in flag: origin+=chr(constant[ord(i)]) print origin
reverse2_final:¶
peid查一下,发现又加了壳。这次是ASPack壳。
跟上面一样,不过这次我直接找了popa,其实原理是一样的。
脱壳完后单步调试。看到明显的提示输入字符串,之后有一些零零碎碎的数据,老规矩C一下,U掉再P,分析函数,再F5即可。
关键函数在1271240处。里面逻辑也很简单,这次主要为了脱壳,就不再分析了。
脚本:
flag="reverse+" origin="" constant=[0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3E,0x3F,0x3C,0x3D,0x3A,0x3B,0x38,0x39,0x26, 0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2E,0x2F,0x2C,0x17,0x14,0x15,0x12,0x13,0x10, 0x11,0x1E,0x1F,0x1C,0x1D,0x1A,0x1B,0x18,0x19,0x06,0x07,0x04,0x05,0x02,0x03,0x00, 0x01,0x0E,0x0F,0x0C,0x46,0x47,0x44,0x45,0x42,0x43,0x40,0x41,0x4E,0x4F,0x5D,0x59, 0x01,0x00,0x00,0x00,0xA8,0x1A,0x73,0x01,0xA0,0x37,0x73,0x01,0x00,0x00,0x00,0x00] def change(one): if one>=0 and one<=9: return one+48 elif one>=10 and one<=15: return one+55 backone=[] for i in flag: temp=ord(i)^0x76 temp=constant.index(temp) backone.append(temp) print hex(temp), print "" backtwo="" for i in range(0,len(backone),4): a=backone[i]<<2 a+=((backone[i+1]&0xf0)>>4)&0x3 b=(backone[i+1]&0xf)<<4 b+=(backone[i+2]&0x3c)>>2 c=(backone[i+2]&0x3)<<6 c+=backone[i+3] print hex(a),hex(b),hex(c) backtwo+=chr(a) backtwo+=chr(b) backtwo+=chr(c)
PS:¶
这个是复现的记录,基本跟做的时候操作一样。不过一开始做的时候有一次栈分析错误,复现的时候没有出现。下面补充下解决方法。
先Options->General->Disassembly->Stack pointer勾选上。之后在汇编指令隔壁能看到多了些数字,这个是当前栈指针相对ebp的偏移。
然后观察函数尾部,对于把平衡栈任务交给被调用者的函数调用,函数执行到retn时,esp应该为0。如果发现esp不为0说明有问题。这时我们可以手动调整。Edit->Functions->Change stack pointer,或者快捷键ALT+K,把指定行的esp调整,直到最后esp偏移为0即可。